package front

import (
	"fmt"
	"geekai/api/dto/common"
	"geekai/api/dto/request"
	"geekai/api/dto/response"
	"geekai/api/handler"
	"geekai/core"
	"geekai/core/types"
	"geekai/model"
	"geekai/service"
	"geekai/service/oss"
	"geekai/service/video"
	"geekai/utils"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"time"
)

type VideoHandler struct {
	handler.BaseHandler
	videoService *video.Service
	uploader     *oss.UploaderManager
	userService  *service.UserService
}

func NewVideoHandler(app *core.AppServer, db *gorm.DB, service *video.Service, uploader *oss.UploaderManager,
	userService *service.UserService) *VideoHandler {
	return &VideoHandler{
		BaseHandler: handler.BaseHandler{
			App: app,
			DB:  db,
		},
		videoService: service,
		uploader:     uploader,
		userService:  userService,
	}
}

// LumaCreate godoc
// @Summary      Luma生成视频
// @Description  Luma生成视频
// @Tags         前台-Luma视频
// @Accept       json
// @Produce      json
// @Param        req   body     request.LumaCreateReq  true  "生成视频参数"
// @Success      200  {object}  types.BizVo{}
// @Router       /api/video/luma/create [post]
// @Security ApiKeyAuth
func (h *VideoHandler) LumaCreate(c *gin.Context) {
	var data request.LumaCreateReq

	if err := c.ShouldBindJSON(&data); err != nil {
		utils.ERROR(c, types.InvalidArgs)
		return
	}

	user, err := h.GetLoginUser(c)
	if err != nil {
		utils.NotAuth(c)
		return
	}

	if user.Power < h.App.SysConfig.LumaPower {
		utils.ERROR(c, "您的算力不足，请充值后再试！")
		return
	}

	if data.Prompt == "" {
		utils.ERROR(c, "提示词不能为空")
		return
	}

	userId := int(h.GetLoginUserId(c))
	params := types.VideoParams{
		PromptOptimize: data.ExpandPrompt,
		Loop:           data.Loop,
		StartImgURL:    data.FirstFrameImg,
		EndImgURL:      data.EndFrameImg,
	}

	task := types.VideoTask{
		ClientId:         data.ClientId,
		UserId:           userId,
		Type:             types.VideoLuma,
		Prompt:           data.Prompt,
		Params:           params,
		TranslateModelId: h.App.SysConfig.TranslateModelId,
	}

	job := model.VideoJob{
		UserId:   uint(userId),
		Type:     types.VideoLuma,
		Prompt:   data.Prompt,
		Power:    h.App.SysConfig.LumaPower,
		TaskInfo: utils.JsonEncode(task),
	}

	tx := h.DB.Create(&job)
	if tx.Error != nil {
		utils.ERROR(c, "创建视频任务失败："+err.Error())
		return
	}

	task.Id = job.Id
	h.videoService.PushTask(task)

	// 扣减算力
	err = h.userService.DecreasePower(int(job.UserId), job.Power, model.PowerLog{
		Type:   types.PowerConsume,
		Model:  "luma",
		Remark: fmt.Sprintf("Luma 文生视频，任务ID：%d", job.Id),
	})

	if err != nil {
		utils.ERROR(c, "扣减算力失败："+err.Error())
		return
	}
	utils.SUCCESS(c)
}

// List godoc
// @Summary      查询视频任务列表
// @Description  查询视频任务列表
// @Tags         前台-Luma视频
// @Accept       json
// @Produce      json
// @Param        page        query     int     true   "页码"
// @Param        page_size   query     int     true   "每页数量"
// @Param        type   	 query     string  false  "任务类型"
// @Param        all   	 	 query     boolean     false  "是否发布"
// @Success      200  {object}  types.BizVo{data=response.Page}
// @Router       /api/video/list [get]
// @Security ApiKeyAuth
func (h *VideoHandler) List(c *gin.Context) {
	userId := h.GetLoginUserId(c)
	t := h.GetTrim(c, "type")
	page := h.GetInt(c, "page", 1)
	pageSize := h.GetInt(c, "page_size", 20)
	all := h.GetBool(c, "all")
	session := h.DB.Session(&gorm.Session{}).Where("user_id", userId)
	if t != "" {
		session = session.Where("type", t)
	}

	if all {
		session = session.Where("publish", 0).Where("progress", 100)
	} else {
		session = session.Where("user_id", userId)
	}

	// 统计总数
	var total int64
	session.Model(&model.VideoJob{}).Count(&total)

	if page > 0 && pageSize > 0 {
		offset := (page - 1) * pageSize
		session = session.Offset(offset).Limit(pageSize)
	}

	var list []model.VideoJob
	err := session.Order("id desc").Find(&list).Error
	if err != nil {
		utils.ERROR(c, "查询数据库出现错误："+err.Error())
		return
	}

	items := make([]common.VideoJob, 0)
	for _, v := range list {
		var item common.VideoJob
		err = utils.CopyObject(v, &item)
		if err != nil {
			log.Errorf("拷贝数据出现错误：%v", err)
			continue
		}
		item.CreatedAt = v.CreatedAt.Unix()
		if item.VideoURL == "" {
			item.VideoURL = v.WaterURL
		}
		items = append(items, item)
	}
	utils.SUCCESS(c, response.NewPage(total, page, pageSize, items))
}

// Remove godoc
// @Summary      删除视频任务
// @Description  删除视频任务
// @Tags         前台-Luma视频
// @Accept       json
// @Produce      json
// @Param        id        query     int     true   "任务Id"
// @Success      200  {object}  types.BizVo{}
// @Router       /api/video/remove [get]
// @Security ApiKeyAuth
func (h *VideoHandler) Remove(c *gin.Context) {
	id := h.GetInt(c, "id", 0)
	userId := h.GetLoginUserId(c)
	var job model.VideoJob
	err := h.DB.Where("id = ?", id).Where("user_id = ?", userId).First(&job).Error
	if err != nil {
		utils.ERROR(c, "查询任务出现错误："+err.Error())
		return
	}

	// 只有失败任务和超时的任务才能删除
	if !(job.Progress == service.FailTaskProgress || time.Now().After(job.CreatedAt.Add(time.Minute*30))) {
		utils.ERROR(c, "只有失败和超时(30分钟)的任务才能删除！")
		return
	}

	// 删除任务
	err = h.DB.Delete(&job).Error
	if err != nil {
		utils.ERROR(c, "删除任务数据库出错："+err.Error())
		return
	}
	// 删除文件
	_ = h.uploader.GetUploadHandler().Delete(job.CoverURL)
	_ = h.uploader.GetUploadHandler().Delete(job.VideoURL)
}

// Publish godoc
// @Summary      发布或者取消发布视频
// @Description  发布或者取消发布视频
// @Tags         前台-Luma视频
// @Accept       json
// @Produce      json
// @Param        id        		 query     int     	   true   "任务Id"
// @Param        publish   	 	 query     boolean     true  "是否发布"
// @Success      200  {object}  types.BizVo{}
// @Router       /api/video/publish [get]
// @Security ApiKeyAuth
func (h *VideoHandler) Publish(c *gin.Context) {
	id := h.GetInt(c, "id", 0)
	userId := h.GetLoginUserId(c)
	publish := h.GetBool(c, "publish")
	var job model.VideoJob
	// 查询任务信息
	err := h.DB.Where("id = ?", id).Where("user_id", userId).First(&job).Error
	if err != nil {
		utils.ERROR(c, "查询任务时出错："+err.Error())
		return
	}

	err = h.DB.Model(&job).UpdateColumn("publish", publish).Error
	if err != nil {
		utils.ERROR(c, "操作数据库出现错误："+err.Error())
		return
	}
	utils.SUCCESS(c)
}
